use std::{fs, io::Write, sync::{OnceLock,Mutex}};
use chrono::{DateTime,Local};
use crate::defines::*;

const LOG_DATETIME_FORMAT:&str = "%Y-%m-%d %H:%M:%S";
const LOG_SPLIT_LINE:&str = "\n================================\n";

static FILE:OnceLock<Mutex<fs::File>> = OnceLock::new();

fn adjust_size(path:&str) -> Result<()> {
    if let Ok(metadata) = fs::metadata(path) {
        if metadata.len() as usize > LOG_MAX_BYTES {
            fs::write(path, b"THIS LOG CONTENT HAS RESET\n")?;
        }
    }
    Ok(())
}

pub fn _init() -> Result<()> {
    assert!(FILE.get().is_none());
    adjust_size(PATH_LOG)?;
    let mut file = fs::File::options().append(true).create(true).open(PATH_LOG)?;
    FILE.get_or_init(|| {
        file.write(LOG_SPLIT_LINE.as_bytes()).unwrap();
        Mutex::new(file)
    });
    Ok(())
}

#[cfg(test)]
pub fn _init_for_test() -> Result<()> {
    if FILE.get().is_none() {
        _init()
    } else {
        Ok(())
    }
}

pub fn line(level:&str, msg:&str) {
    assert!(FILE.get().is_some());
    let now:DateTime<Local> = Local::now();
    let mut buf:Vec<u8> = Vec::new();
    write!(&mut buf, "[{}]{}: {}\n", now.format(LOG_DATETIME_FORMAT), level, msg).unwrap();
    let mut file = FILE.get().unwrap().lock().unwrap();
    file.write(&buf[..]).unwrap();
    file.flush().unwrap();
}

// FUCK RUST MACRO
macro_rules! LEVEL_JOIN {
    ($level:expr, $name:ident) => {
        macro_rules! $name {
            ($fmt:literal, $a:expr, $b:expr, $c:expr, $d:expr, $e:expr, $f:expr, $g:expr, $h:expr, $i:expr) => {
                log::line($level, &format!($fmt, $a, $b, $c, $d, $e, $f, $g, $h, $i))
            };
            ($fmt:literal, $a:expr, $b:expr, $c:expr, $d:expr, $e:expr, $f:expr, $g:expr, $h:expr) => {
                log::line($level, &format!($fmt, $a, $b, $c, $d, $e, $f, $g, $h))
            };
            ($fmt:literal, $a:expr, $b:expr, $c:expr, $d:expr, $e:expr, $f:expr, $g:expr) => {
                log::line($level, &format!($fmt, $a, $b, $c, $d, $e, $f, $g))
            };
            ($fmt:literal, $a:expr, $b:expr, $c:expr, $d:expr, $e:expr, $f:expr) => {
                log::line($level, &format!($fmt, $a, $b, $c, $d, $e, $f))
            };
            ($fmt:literal, $a:expr, $b:expr, $c:expr, $d:expr, $e:expr) => {
                log::line($level, &format!($fmt, $a, $b, $c, $d, $e))
            };
            ($fmt:literal, $a:expr, $b:expr, $c:expr, $d:expr) => {
                log::line($level, &format!($fmt, $a, $b, $c, $d))
            };
            ($fmt:literal, $a:expr, $b:expr, $c:expr) => {
                log::line($level, &format!($fmt, $a, $b, $c))
            };
            ($fmt:literal, $a:expr, $b:expr) => {
                log::line($level, &format!($fmt, $a, $b))
            };
            ($fmt:literal, $a:expr) => {
                log::line($level, &format!($fmt, $a))
            };
            ($msg:expr) => {
                log::line($level, $msg)
            };
        }
        pub(crate) use $name;
    };
}

#[cfg(debug_assertions)]
LEVEL_JOIN!(&format!("Info<{} {}:{}>", file!(), line!(), column!()), info);
#[cfg(not(debug_assertions))]
LEVEL_JOIN!("Info", info);

#[cfg(debug_assertions)]
LEVEL_JOIN!(&format!("Warning<{} {}:{}>", file!(), line!(), column!()), warning);
#[cfg(not(debug_assertions))]
LEVEL_JOIN!("Warning", warning);

#[cfg(debug_assertions)]
LEVEL_JOIN!(&format!("Error<{} {}:{}>", file!(), line!(), column!()), error);
#[cfg(not(debug_assertions))]
LEVEL_JOIN!("Error", error);

#[cfg(test)]
LEVEL_JOIN!(&format!("Test<{}>", file!()), test);

#[cfg(debug_assertions)]
LEVEL_JOIN!(&format!("Debug<{} {}:{}>", file!(), line!(), column!()), debug);
#[cfg(not(debug_assertions))]
macro_rules! debug {
    () => {};
    ($($_:tt)*) => {}
}
#[cfg(not(debug_assertions))]
pub(crate) use debug;
